package com.yaorange.certificate.service.impl;

import java.io.*;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.github.pagehelper.PageHelper;
import com.yaorange.certificate.domain.NjtCertificateCount;
import com.yaorange.certificate.domain.NjtUserPerfect;
import com.yaorange.certificate.mapper.NjtCertificateCountMapper;
import com.yaorange.certificate.mapper.NjtUserPerfectMapper;
import com.yaorange.certificate.utils.QiniuUtils;
import com.yaorange.certificate.utils.QrCodeCreateUtil;
import com.yaorange.certificate.vo.BatchPrintCertificateVo;
import com.yaorange.common.exception.ServiceException;
import com.yaorange.common.utils.DateUtils;
import com.yaorange.common.utils.SecurityUtils;
import com.yaorange.common.utils.bean.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.yaorange.certificate.mapper.NjtCertificateMapper;
import com.yaorange.certificate.domain.NjtCertificate;
import com.yaorange.certificate.service.INjtCertificateService;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

/**
 * 合格证打印数据Service业务层处理
 *
 * @author loong
 * @date 2025-01-20
 */
@Slf4j
@Service
public class NjtCertificateServiceImpl implements INjtCertificateService {
    public static final String TEMP = "d://temp/";
    @Autowired
    private NjtCertificateMapper njtCertificateMapper;
    @Autowired
    private NjtCertificateCountMapper njtCertificateCountMapper;
    @Autowired
    private NjtUserPerfectMapper njtUserPerfectMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private QiniuUtils qiniuUtils;

    //定义一个线程池 核心线程数*2 最大线程数100 队列数1000  拒绝策略CallerRunPolicy 空闲时间1秒 线程工厂
    private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 100, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadFactory() {
        AtomicInteger id = new AtomicInteger(1);

        @Override
        public Thread newThread(@NotNull Runnable r) {
            return new Thread(r, "batch code task" + id.getAndIncrement());
        }
    }, new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * 查询合格证打印数据
     *
     * @param id 合格证打印数据主键
     * @return 合格证打印数据
     */
    @Override
    public NjtCertificate selectNjtCertificateById(Long id) {
        return njtCertificateMapper.selectNjtCertificateById(id);
    }

    /**
     * 查询合格证打印数据列表
     *
     * @param njtCertificate 合格证打印数据
     * @return 合格证打印数据
     */
    @Override
    public List<NjtCertificate> selectNjtCertificateList(NjtCertificate njtCertificate) {
        return njtCertificateMapper.selectNjtCertificateList(njtCertificate);
    }

    /**
     * 新增合格证打印数据
     *
     * @param njtCertificate 合格证打印数据
     * @return 结果
     */
    @Override
    public int insertNjtCertificate(NjtCertificate njtCertificate) {
        njtCertificate.setCreateTime(DateUtils.getNowDate());
        return njtCertificateMapper.insertNjtCertificate(njtCertificate);
    }

    /**
     * 修改合格证打印数据
     *
     * @param njtCertificate 合格证打印数据
     * @return 结果
     */
    @Override
    @Transactional
    public int updateNjtCertificate(NjtCertificate njtCertificate) {
        njtCertificate.setUpdateTime(DateUtils.getNowDate());
        return njtCertificateMapper.updateNjtCertificate(njtCertificate);
    }

    /**
     * 批量删除合格证打印数据
     *
     * @param ids 需要删除的合格证打印数据主键
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteNjtCertificateByIds(Long[] ids) {
        int i=1/0;
        return njtCertificateMapper.deleteNjtCertificateByIds(ids);
    }

    /**
     * 删除合格证打印数据信息
     *
     * @param id 合格证打印数据主键
     * @return 结果
     */
    @Override
    public int deleteNjtCertificateById(Long id) {
        return njtCertificateMapper.deleteNjtCertificateById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> batchCode(BatchPrintCertificateVo batchPrintCertificateVo) {

        //1.合格证详情表(多条)
        Integer printNumber = batchPrintCertificateVo.getPrintNumber();

        List<String> codeList = new ArrayList<>(printNumber);

        if (ObjectUtils.isEmpty(printNumber)) {
            throw new ServiceException("打印数量不能为空");
        }
        List<NjtCertificate> tempList = new CopyOnWriteArrayList<>();
        String areaCode = getAreaCode();
        String dateStr = getDateStr();
        Long userId = SecurityUtils.getUserId();
        String username = SecurityUtils.getLoginUser().getUsername();
        BigDecimal totalWeight = new BigDecimal(printNumber).multiply(new BigDecimal(batchPrintCertificateVo.getProductWeight()));

        List<Future<String>> futureList = new ArrayList<>();
        for (int i = 0; i < printNumber; i++) {
            //将每个合格证的创建，包装成一个任务
            Callable<String> callable = () -> {
                //每循环一次就需要生产一条合格证数据(临时)
                NjtCertificate temp = new NjtCertificate();
                temp.setUserId(userId);
                temp.setCode(this.getCode(areaCode, dateStr));
                temp.setQrcode(this.getQrCode(temp.getCode()));
                temp.setBatchType("0");
                temp.setDelFlag(1L);
                temp.setPrintNumber(0L);
                temp.setProduceAddress(batchPrintCertificateVo.getProduceAddress());
                temp.setProductName(batchPrintCertificateVo.getProductName());
                temp.setScanNum(0);
                temp.setProductWeight(new BigDecimal(batchPrintCertificateVo.getProductWeight()));
                temp.setTestBy(batchPrintCertificateVo.getTestBy());
                temp.setTestReport(batchPrintCertificateVo.getTestReport());

                //保存到mysql表中
                tempList.add(temp);
//                codeList.add(temp.getCode());
                return temp.getCode();
            };
            //提交任务给线程池
            Future<String> future = threadPoolExecutor.submit(callable);
            futureList.add(future);
        }

        //收集线程池中每个线程生产的code
        futureList.forEach(stringFuture -> {
            try {
                String code = stringFuture.get();
                codeList.add(code);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        });
        //批量保存
        njtCertificateMapper.insertBatch(tempList);
        //2.合格证统计表(1条)
        NjtCertificateCount njtCertificateCount = new NjtCertificateCount();
        BeanUtils.copyBeanProp(njtCertificateCount, batchPrintCertificateVo);
        njtCertificateCount.setUserId(userId);
        njtCertificateCount.setMobilePhone(username);
        //累计重量
        njtCertificateCount.setProductWeight(totalWeight);
        njtCertificateCountMapper.insertNjtCertificateCount(njtCertificateCount);
        return codeList;
    }

    private String getQrCode(String code) {
        //二维码内容为前端固定网页地址+合格证编码，如：http://localhost/scanCodeTrace?code=5101172023080800027
        String qrContent = "http://localhost/scanCodeTrace?code=" + code;
        //1.生成一个二维码
        File file = QrCodeCreateUtil.doCreateQrCode("d://static/temp", code, qrContent, 300, true);
        //2.上传二维码到七牛云,返回完整的图片地址
        try {
            //获取到file文件的字节数组
            byte[] bytes = FileUtils.readFileToByteArray(file);
            qiniuUtils.uploadFile(bytes, file.getName());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //删除本地临时文件
            file.delete();
        }
        return QiniuUtils.QINIU_BASE_URL + file.getName();
    }

    private String getDateStr() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyddMM");
        return dateFormat.format(new Date());
    }

    private String getAreaCode() {
        Long userId = SecurityUtils.getUserId();
        NjtUserPerfect njtUserPerfect = njtUserPerfectMapper.selectNjtUserPerfectById(userId);
        if (ObjectUtils.isEmpty(njtUserPerfect)) {
            throw new ServiceException("用户完善信息不能为空");
        }
        return njtUserPerfect.getAddressCode().substring(0, 6);
    }


    private String getCode(String areaCode, String dateStr) {
        //会员完善信息表地址编码前6位（510117）+ 年月日（20230101）+ 6位递增数（000001）
        Long code = stringRedisTemplate.opsForValue().increment("certificate_code:" + dateStr);
        //将code使用0补全成6位数,使用String.format实现,比如 code为1时，转换成000001
        code = Long.valueOf(String.format("%06d", code));
        String result = areaCode + dateStr + code;
        log.debug("生成合格证:{}", result);
        return result;
    }

    //创建一个单线程池(使用jdk内置)
    private ThreadPoolExecutor singleExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());

    //定义一个整形用于记录导出百分比
    private volatile int percent = 0;
    //    private AtomicInteger percent = new AtomicInteger(0);
//    private Lock lock = new ReentrantLock();
    private Semaphore lock = new Semaphore(3);
    @Override
    public String exportAll(HttpServletResponse response) {
        if (!lock.tryAcquire(1)) {
            throw new ServiceException("当前有线程正在导出,请稍后再试");
        }
        ClassPathResource resource = new ClassPathResource("excel/certificate_print_template.xlsx");
        String filename = UUID.randomUUID() + ".xlsx";
        String tempFilePath = TEMP + filename;
        singleExecutor.execute(() -> {
            //1.分批次读取mysql的数据保存到excel文档中
            int pageSize = 20000;
            int size = 0;
            int pageNum = 1;
            log.debug("开始全量导出合格证打印数据到excel...");
            //查询出总记录数
            int total = njtCertificateMapper.selectNjtCertificateCount();
            int exportedSize = 0;
            //重新读取临时文件，再临时文件基础上追加
            try (
                    OutputStream outputStream = Files.newOutputStream(Paths.get(tempFilePath));
                    XSSFWorkbook workbook = new XSSFWorkbook(resource.getInputStream());
                    SXSSFWorkbook sheets = new SXSSFWorkbook(workbook, pageSize)
            ) {
                Sheet sheet = sheets.getSheetAt(0);
                do {
                    PageHelper.startPage(pageNum, pageSize);
                    List<NjtCertificate> list = njtCertificateMapper.selectNjtCertificateList(new NjtCertificate());
                    size = list.size();
                    exportedSize += size;
                    percent = (exportedSize * 100) / total;
                    if (CollectionUtils.isEmpty(list)) {
                        break;
                    }
                    log.debug("当前第{}页,一共{}条数据,累计导出{}条,百分比{}", pageNum, size, exportedSize, percent);
                    this.writeData(sheet, list);
                    pageNum++;
                } while (size == pageSize);
                sheets.write(outputStream);
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                lock.release();
            }
        });
        log.debug("全量导出合格证打印数据到excel结束");
        return filename;


    }

    @Override
    public int queryProgress() {
        return percent;
    }

    @Override
    public void download(String tempFile, HttpServletResponse response) {
        //2.将完整的excel文档发送给客户端
        log.debug("开始下载");
        //准备一个字节输入流去读取tempFilePath文件内容，每次读1kb，读到后将数据通过response发送给客户端
        String tempFilePath = TEMP + tempFile;
        try (
                InputStream inputStream = Files.newInputStream(Paths.get(tempFilePath));
        ) {
            ServletOutputStream output = response.getOutputStream();
            response.setContentType("application/octet-stream; charset=UTF-8");
            IOUtils.copy(inputStream, output);
            output.flush();
        } catch (Exception e) {
            log.error("下载异常", e);
            throw new ServiceException("下载异常");
        }
        log.debug("下载结束");
    }

    //分批次写入到sheet
    private void writeData(Sheet sheet, List<NjtCertificate> list) {
        //读取sheet最后一行数
        int lastRowNum = sheet.getLastRowNum();
        if (lastRowNum < 0) {
            lastRowNum = 0;
        }
        //遍历list,将每个NjtCertificate对象转成一行数据
        for (NjtCertificate njtCertificate : list) {
//            NjtCertificateVo njtCertificateVo = (NjtCertificateVo)njtCertificate;
            Row row = sheet.createRow(++lastRowNum);
            Cell cell = row.createCell(0);
            cell.setCellValue(njtCertificate.getUserId());

            cell = row.createCell(1);
            cell.setCellValue(njtCertificate.getProductName());

            cell = row.createCell(2);
            cell.setCellValue(njtCertificate.getProductWeight().toString());

            cell = row.createCell(3);
            cell.setCellValue(njtCertificate.getUnit());
        }
    }
}
